#pragma once
#include<iostream>
#include<memory>
#include<sys/epoll.h>
#include"Socket.hpp"
#include"Epoller.hpp"
#include"nocopy.hpp"

const static uint16_t default_port=8080;
uint32_t EVENT_IN=EPOLLIN;
uint32_t EVENT_OUT=EPOLLOUT;

using std::cout;
using std::endl;

class EpollServer:public nocopy
{
    const static int num=64;
public:
    EpollServer(uint16_t port=default_port)
    :_port(port),
    _listensocket_ptr(new Sock()),
    _epoller_ptr(new Epoller())
    {}

    void Init()
    {
        _listensocket_ptr->Socket();
        _listensocket_ptr->Bind(_port);
        _listensocket_ptr->Listen();

        cout<<"socket init successfully,sockfd is "<<_listensocket_ptr->sockfd()<<endl;
    }

    void Handle_Event(struct epoll_event* revs,int size)
    {
        for(int i=0;i<size;i++)
        {
            int fd=revs[i].data.fd;//内核已经就绪的fd
            uint32_t event=revs[i].events;//fd对应的就绪事件
            if(event & EVENT_IN)
            {
                if(fd==_listensocket_ptr->sockfd())
                {
                    //捕获到一个新链接
                    string client_ip;
                    uint16_t client_port;
                    int new_sockfd=_listensocket_ptr->Accept(client_ip,client_port);
                    _epoller_ptr->EpollCtrl(EPOLL_CTL_ADD,new_sockfd,EVENT_IN);//将新链接的fd放入epoll的红黑树中，wait对方给“我”发数据
                }
                else 
                {
                    //该fd的read已经就绪
                    char buffer[1024];
                    ssize_t n=read(fd,buffer,sizeof(buffer)-1);//bug  tcp面向字节流，读取的报文可能不完整——>Encode、Decode + 序列化、反序列化
                    if(n>0)
                    {
                        buffer[n]='\0';//buffer中含回车
                        cout<<"receive a new data:"<<buffer<<endl;

                        //write back
                        string echo_result="server echo$ ";
                        echo_result+=buffer;
                        write(fd,echo_result.c_str(),echo_result.size());
                    }
                    else if(n==0)
                    {
                        cout<<"counterpart have closed sock, so i will close it..."<<endl;
                        _epoller_ptr->EpollCtrl(EPOLL_CTL_DEL,fd,0);//将一个fd从epoll的红黑树中移除的前提是：该fd合法！若移除非法fd，会报错
                        close(fd);//所以，先DEL，后close！！
                    }
                    else 
                    {
                        cerr<<"read failed，so i will close it..."<<endl;
                        _epoller_ptr->EpollCtrl(EPOLL_CTL_DEL,fd,0);
                        close(fd);
                    }
                }
            }
            else if(event & EVENT_OUT)
            {}
            else 
            {}
        }
    }

    void Start()
    {
        _epoller_ptr->EpollCtrl(EPOLL_CTL_ADD,_listensocket_ptr->sockfd(),EVENT_IN);//把“监听”fd放到epoll红黑树中，并让epoll关注fd的“读事件”

        struct epoll_event revs[num];
        for(; ;)
        {
            int n=_epoller_ptr->EpollWait(revs,num);
            if(n>0)
            {
                //TODO
                cout<<"捕获到一下新的链接，sockfd is:"<<revs[0].data.fd<<endl;//在没有将fd拿到上层前，epollwait会一直处于就绪状态
                Handle_Event(revs,n);
            }
            else if(n==0)
            {
                cout<<"epoll wait time out..."<<endl;
            }
            else 
            {
                cout<<"epoll wait fail..."<<endl;
            }
        }
    }

    ~EpollServer()
    {
        _listensocket_ptr->Close();
    }
private:
    shared_ptr<Sock> _listensocket_ptr;
    shared_ptr<Epoller> _epoller_ptr;
    uint16_t _port;
};